#ifndef bitboard_h
#define bitboard_h

#include <stdint.h>


// This is a macro so that the compiler can use the best return type.
#define BB_TEST(bb, pos) (((bb) & BB_SINGLE(pos)) != 0)
#define BB_SET(bb, pos) ((bb) |= BB_SINGLE(pos))
#define BB_CLEAR(bb, pos) ((bb) &= ~BB_SINGLE(pos))
#define BB_FLIP(bb, pos) ((bb) ^= BB_SINGLE(pos))
#define BB_SINGLE(pos) (UINT64_C(1) << (pos))

#define BB_RANK_1 UINT64_C(0xff)
#define BB_RANK_2 (BB_RANK_1 << 8)
#define BB_RANK_3 (BB_RANK_1 << 16)
#define BB_RANK_4 (BB_RANK_1 << 24)
#define BB_RANK_5 (BB_RANK_1 << 32)
#define BB_RANK_6 (BB_RANK_1 << 40)
#define BB_RANK_7 (BB_RANK_1 << 48)
#define BB_RANK_8 (BB_RANK_1 << 56)

#define BB_FILE_A UINT64_C(0x101010101010101)
#define BB_FILE_B (BB_FILE_A << 1)
#define BB_FILE_C (BB_FILE_A << 2)
#define BB_FILE_D (BB_FILE_A << 3)
#define BB_FILE_E (BB_FILE_A << 4)
#define BB_FILE_F (BB_FILE_A << 5)
#define BB_FILE_G (BB_FILE_A << 6)
#define BB_FILE_H (BB_FILE_A << 7)

#define BB_RANK(n) (BB_RANK_1 << ( (n) << 3))
#define BB_FILE(n) (BB_FILE_A << (n) )

#define BB_DARK_SQUARES UINT64_C(0xaa55aa55aa55aa55)
#define BB_LIGHT_SQUARES UINT64_C(0x55aa55aa55aa55aa)

#define BB_LSB(bb) (__builtin_ctzll(bb))
#define BB_MSB(bb) (63 - __builtin_clzll(bb))
#define BB_SELECT_LSB(bb) ((bb) & (-bb))
#define BB_SELECT_MSB(bb) ((bb) & (BB_SINGLE(63) >> __builtin_clzll(bb)))
#define BB_CLEAR_LSB(bb) ((bb) &= ((bb) - 1))
#define BB_POPCOUNT(bb) (popcount_bits(bb))
#define BB_IS_NOT_SINGLE(bb) (((bb) & ((bb) - 1)) != 0)


typedef uint64_t bitboard_t;


static inline int popcount_bits(bitboard_t bb)
{
  bb = ((bb & UINT64_C(0x5555555555555555)) +
	((bb >> 1) & UINT64_C(0x5555555555555555)));
  bb = ((bb & UINT64_C(0x3333333333333333)) +
	((bb >> 2) & UINT64_C(0x3333333333333333)));
  bb = ((bb & UINT64_C(0xf0f0f0f0f0f0f0f)) +
	((bb >> 4) & UINT64_C(0xf0f0f0f0f0f0f0f)));
  bb = ((bb & UINT64_C(0xff00ff00ff00ff)) +
	((bb >> 8) & UINT64_C(0xff00ff00ff00ff)));
  bb = ((bb & UINT64_C(0xffff0000ffff)) +
	((bb >> 16) & UINT64_C(0xffff0000ffff)));
  return (bb & UINT64_C(0xffffffff)) + (bb >> 32);
}


static inline bitboard_t bb_fill_w(bitboard_t bb,
				   bitboard_t occ)
{
  occ &= ~BB_FILE_H;
  bb |= (bb >> 1) & ~occ;
  occ |= (occ >> 1);
  bb |= (bb >> 2) & ~occ;
  occ |= (occ >> 2);
  return bb | ((bb >> 4) & ~occ);
}


static inline bitboard_t bb_fill_e(bitboard_t bb,
				   bitboard_t occ)
{
  occ &= ~BB_FILE_A;
  bb |= (bb << 1) & ~occ;
  occ |= (occ << 1);
  bb |= (bb << 2) & ~occ;
  occ |= (occ << 2);
  return bb | ((bb << 4) & ~occ);
}


static inline bitboard_t bb_fill_n(bitboard_t bb,
				   bitboard_t occ)
{
  bb |= (bb << 8) & ~occ;
  occ |= (occ << 8);
  bb |= (bb << 16) & ~occ;
  occ |= (occ << 16);
  return bb | ((bb << 32) & ~occ);
}


static inline bitboard_t bb_fill_s(bitboard_t bb,
				   bitboard_t occ)
{
  bb |= (bb >> 8) & ~occ;
  occ |= (occ >> 8);
  bb |= (bb >> 16) & ~occ;
  occ |= (occ >> 16);
  return bb | ((bb >> 32) & ~occ);
}


#endif  // bitboard_h
